package org.jugile.util;

/*

Copyright (C) 2007-2011 Jukka Rahkonen  email: jukka.rahkonen@iki.fi

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

import java.util.ArrayList;
import java.util.List;

/**
 * ""
 * 
 * @author jukka.rahkonen@iki.fi
 */
public class Wiki extends Jugile {

	enum State {
		LINESTART,
		LINE,
	}

	enum Type {
		B,
		I,
		LI, // list item
		LIN, // list item with number
		IND, // indent
		LINK,
		EMPTY, // empty line
		STR, // just string content
		URL, // syntax: http://xxxxxxx
	}
	
	public class Token {
		private Type type;
		private StringBuffer content = new StringBuffer();
		private int indent;
		
		public Token(Type t) {
			this.type = t;
		}
		public void append(char ch) {
			content.append(ch);
		}
		
	}
	
	public class Line {
		private String line;
		public Line(String line) { 
			this.line = line;
		}
		public char charAt(int idx) {
			if (idx > line.length() || idx < 0) return 0;
			return line.charAt(idx); 
		}
		public int length() { return line.length(); }
		public Token getIndentToken() {
			int indent = indent();
			if (indent > 0) {
				Token t = new Token(Type.IND);
				t.indent = indent;
				return t;
			}
			return null;
		}
		public int indent() {
			for (int i = 0; i < line.length(); i++) {
				if (!Character.isWhitespace(line.charAt(i))) return i;
			}
			return line.length(); // all whites
		}
		public boolean equals(String str, int idx) {
			if ((idx + str.length()) > line.length()) return false;
			if (str.equals(line.substring(idx, idx+str.length()))) return true;
			return false;
		}
	}
	
	public static void main(String args[]) {
		// luetaan wiki.txt
		Blob wiki = new Blob("wiki.txt");
		Wiki w = new Wiki();
		w.parse(wiki);
		w.printTokens();
		w.toHtml();
	}
	
	private List<Token> tokens = new ArrayList<Token>();
	private void addToken(Token t) { if (t != null) tokens.add(t); }
	
	public void parse(Blob b) {
		for (String linetxt : b.getLines()) {
			if (empty(linetxt)) {
				tokens.add(new Token(Type.EMPTY));
				continue;
			}
			Line line = new Line(linetxt);
			// count indent - not fully empty line
			addToken(line.getIndentToken());
			State st = State.LINESTART;
			Token t = new Token(Type.STR);
			for (int idx = line.indent(); idx < line.length(); idx++) {
				if (t == null) t = new Token(Type.STR);
				char ch = line.charAt(idx);
				if (ch == '\\') {
					idx++;
					// add next char to buffer as it is
					ch = line.charAt(idx);
					if (ch != 0) t.append(ch);
					st = State.LINE;
					continue;
				}
				if (st == State.LINESTART) {
					st = State.LINE;
					if (ch == '-') {
						tokens.add(new Token(Type.LI));
						t = null;
						continue;
					}
					if (Character.isDigit(ch)) {
						if (line.charAt(idx+1) == '.') {
							tokens.add(new Token(Type.LIN));
							t = null;
							idx++;
							continue;
						}
					}
				}
				if (ch == '*') {
					if (t != null) tokens.add(t);
					t = null;
					tokens.add(new Token(Type.B));
					continue;
				}
				if (ch == '/') {
					if (line.charAt(idx+1) == '/') {
						if (t != null) tokens.add(t);
						t = null;
						tokens.add(new Token(Type.I));
						idx++;
						continue;
					}
				}
				if (ch == '[') {
					// luetaan kaikki sulkumerkkiin asti
					int end = linetxt.indexOf(']', idx);
					if (end > idx) {
						if (t != null) tokens.add(t);
						t = new Token(Type.LINK);
						t.content.append(linetxt.substring(idx+1, end));
						tokens.add(t);
						t = null;
						idx = end;
						continue;
					}
				}
				if (ch == 'h') {
					if (line.equals("http://", idx)) {
						if (t != null) tokens.add(t);
						t = new Token(Type.URL);
						int end = linetxt.indexOf(" ", idx+7);
						if (end < idx) end = linetxt.length();
						t.content.append(linetxt.substring(idx, end));
						tokens.add(t);
						t = null;
						idx = end;
						continue;
					}
				}
				t.append(ch); // default case
			}
			if (t != null) tokens.add(t);
		}
	}
	
	public void printTokens() {
		for (Token t : tokens) {
			String ind = "";
			if (t.type == Type.IND) ind = " "+t.indent;
			print(t.type + ind + ": " + t.content);
		}
	}

	enum ParseState {
		START,
		B,
		I,
		NORMAL
	}
	
	public Node toHtml() {
		ParseState ps = ParseState.START;
		Node root = new Node("div");
		Node n = root.add("p");
		for (Token t : tokens) {
			if (ps == ParseState.START) {
				if (t.type == Type.EMPTY) continue;
				ps = ParseState.NORMAL;
			}
			if (t.type == Type.EMPTY) {
				ps = ParseState.NORMAL;
				n = root.add("p");
				continue;
			}
			if (t.type == Type.STR) {
				n.add("", t.content.toString()); // text node
				continue;
			}
		}
		print(root.render());
		return root;
	}
	
}
